package com.ruoyi.web.controller.app;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.app.domain.bo.AppUserBo;
import com.ruoyi.app.domain.vo.AppAssetVo;
import com.ruoyi.app.domain.vo.AppThirdUserVo;
import com.ruoyi.app.domain.vo.AppUserVo;
import com.ruoyi.app.domain.vo.UserVo;
import com.ruoyi.app.service.IAppAssetService;
import com.ruoyi.app.service.IAppThirdUserService;
import com.ruoyi.app.service.IAppUserService;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.SmsLoginBody;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.Md5Util;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.VerifyCodeUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.im.core.TxTemplate;
import com.ruoyi.sms.config.properties.SmsProperties;
import com.ruoyi.sms.core.PnsTemplate;
import com.ruoyi.sms.entity.SmsResult;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.AppLoginService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysNoticeService;
import com.ruoyi.web.service.AsyncCaller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.query.Param;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.validation.Valid;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * APP登录验证
 *
 * @author Lion Li
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/app")
public class AppLoginController {

    @Autowired
    private RestTemplate restTemplate;
    private final IAppUserService appUserService;
    private final AppLoginService appLoginService;
    private final IAppAssetService appAssetService;
    private final ISysNoticeService sysNoticeService;
    private final ISysConfigService iSysConfigService;
    private final IAppThirdUserService appThirdUserService;
    private final SmsProperties smsProperties;
    private final AsyncCaller asyncCaller;


    /**
     * 账号密码登录
     * 登录方法
     *
     * @param loginBody 用户信息
     * @return
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @PostMapping("login")
    public R<Map<String, Object>> login(@Valid LoginBody loginBody) {
        Map<String, Object> ajax = new HashMap<>();
        // 生成令牌
        String token = appLoginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode());
        UserVo userVo = appUserService.queryByIdUserVo(LoginHelper.getUserId());
        createImUser(userVo);
        ajax.put(Constants.TOKEN, token);
        ajax.put(Constants.USER_INFO, userVo);
        return R.ok(ajax);
    }

    /**
     * 手机验证码登录
     * 登录方法
     *
     * @param smsLoginBody 短信登录信息
     * @return 结果
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @PostMapping("mobileLogin")
    public R<Map<String, Object>> mobileLogin(SmsLoginBody smsLoginBody) {
        Map<String, Object> ajax = new HashMap<>();
        // 生成令牌
        String token = appLoginService.login(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode());
        UserVo userVo = appUserService.queryByIdUserVo(LoginHelper.getUserId());
        createImUser(userVo);
        ajax.put(Constants.TOKEN, token);
        ajax.put(Constants.USER_INFO, userVo);
        return R.ok(ajax);
    }


    /**
     * 根据手机号查询是否设置过密码
     *
     * @param loginBody 根据手机号查询是否设置过密码
     * @return
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @PostMapping("selSetPasswordByPhone")
    public R<?> selSetPasswordByPhone(@Valid LoginBody loginBody) {
            //根据手机号查询用户
        AppUserVo userVo = appUserService.queryByPhone(loginBody.getUsername());
        if (Objects.isNull(userVo)){
            return R.ok("该手机号未注册",1);
        }
        if (userVo.getPassword()==null){
            return R.ok(2);
        }
        return R.ok(3);
    }


    /**
     * 手机验证码注册
     * 注册方法
     *
     * @param loginBody 登录信息
     * @return 结果
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @PostMapping("register")
    public R<Map<String, Object>> register(@Valid LoginBody loginBody) {
        if (StringUtils.isBlank(loginBody.getCode())) {
            return R.fail("验证码不能为空!");
        }
        AppUserVo user = appUserService.queryByPhone(loginBody.getUsername());
        if (Objects.nonNull(user)) {
            return R.fail("手机号码已注册!");
        }
        String key = CacheConstants.LOGIN_OUT + loginBody.getUsername();
        Boolean result = RedisUtils.hasKey(key);
        if (result) {
            return R.fail("账号注销1个月后才能再次注册！");
        }
        //查验企业邀请码是否正确

        Map<String, Object> ajax = new HashMap<>(10);
        // 生成令牌
        String token = appLoginService.login(loginBody, loginBody.getCode());
        UserVo userVo = appUserService.queryByIdUserVo(LoginHelper.getUserId());
        createImUser(userVo);
        ajax.put(Constants.TOKEN, token);
        ajax.put(Constants.USER_INFO, userVo);
        return R.ok(ajax);
    }



    /**
     * 发送短信验证码
     *
     * @param phonenumber 手机号码
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @PostMapping("sendMessage")
    public R<Object> sendMessage(
        @RequestParam(name = "phonenumber") String phonenumber,
        @RequestParam(name = "type", defaultValue = "0") Integer type,
        @RequestParam(name = "otherType", defaultValue = "wechat") String otherType
    ) {
        String regex = "^1\\d{10}$";
        if (!Pattern.matches(regex, phonenumber)) {
            return R.fail("手机号码格式错误!");
        }
        if (Constants.FORGET.equals(type)) {
            AppUserVo userVo = appUserService.queryByPhone(phonenumber);
            if (Objects.isNull(userVo)) {
                return R.fail("账号未注册!");
            }
        }
        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
        String sendCode = RedisUtils.getCacheObject(key);
        if (StringUtils.isNotBlank(sendCode)) {
            return R.fail("验证码已发送，请稍后在再试");
        }
        String code = VerifyCodeUtils.generateVerifyNumberCode(4);
        HashMap<String, Object> data = new HashMap<>();
        data.put("appid", smsProperties.getAccessKeyId());
        data.put("key", smsProperties.getAccessKeySecret());
        data.put("code", code);
        data.put("mobile", phonenumber);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<HashMap<String, Object>> formEntity = new HttpEntity<>(data, headers);
        String body = restTemplate.postForEntity(smsProperties.getEndpoint(), formEntity, String.class).getBody();
        JSONObject jsonObject = JSONObject.parseObject(body);
        boolean result = jsonObject.get("code").toString().equals(Constants.FAIL);
        if (result) {
            //缓存短信验证码
            RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.APP_CAPTCHA_EXPIRATION));
            log.info("手机号码[{}]的验证码是：{}", phonenumber, code);
            return R.ok("发送短信成功");
        } else {
            return R.fail("发送短信失败!");
        }
    }


    /**
     * 获取隐私协议和用户协议
     * 3 用户协议
     * 4 隐私政策
     * @param type 协议类型
     * @return
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)
    @GetMapping("notice")
    public R<SysNotice> notice(Integer type) {
        return R.ok(sysNoticeService.queryByType(String.valueOf(type)));
    }


    /**
     * 获取帮助中心列表
     *
     * @param pageQuery 分页信息
     * @return
     */
    @RepeatSubmit(interval = 2000)

    @GetMapping("noticePage")
    public TableDataInfo<SysNotice> noticePage(PageQuery pageQuery) {
        SysNotice sysNotice = new SysNotice();
        sysNotice.setNoticeType("10");
        return sysNoticeService.selectPageNoticeList(sysNotice, pageQuery);
    }

    /**
     * 忘记密码
     *
     * @param phonenumber 手机号码
     * @param password    新密码
     * @param code        验证码
     * @return
     */
    @SaIgnore
    @RepeatSubmit(interval = 2000)

    @PostMapping("password")
    public R<String> getPassword(
        @RequestParam(name = "code") String code,
        @RequestParam(name = "password") String password,
        @RequestParam(name = "phonenumber") String phonenumber
    ) {
        String regex = "^1\\d{10}$";
        if (!Pattern.matches(regex, phonenumber)) {
            return R.fail("手机号码格式错误!");
        }
        if (password.length() < 6) {
            return R.fail("密码长度最少6位!");
        }
        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
        String msCode = RedisUtils.getCacheObject(key);
        if (smsProperties.getEnabled() && StringUtils.isBlank(msCode)) {
            throw new CaptchaExpireException();
        }
        if (smsProperties.getEnabled() && !msCode.equals(code)) {
            throw new CaptchaException();
        }
        AppUserVo user = appUserService.queryByPhone(phonenumber);
        if (Objects.isNull(user)) {
            return R.fail("手机号码错误!");
        }
        RedisUtils.deleteObject(key);
        return R.result(appUserService.updatePassword(user.getId(), password));
    }


    /**
     * APP退出登录
     *
     * @return
     */
    @PostMapping("logOut")
    public R<Object> logOut() {
        AppUserVo appUserVo = appUserService.queryById(LoginHelper.getUserId());
        if (Objects.nonNull(appUserVo)) {
            appUserService.updateOnline(appUserVo.getId());
        }
        appLoginService.logout();
        return R.ok("退出成功");
    }


    /**
     * APP设置密码
     *
     * @return
     */
    @PostMapping("setPassword")
    public R<Object> setPassword(@Param("password") String password,
                                 @Param("surePassword") String surePassword) {
        if (!password.equals(surePassword)){
            return R.fail("密码不一致!");
        }
        AppUserVo appUserVo = appUserService.queryById(LoginHelper.getUserId());
        if (Objects.nonNull(appUserVo)) {
            appUserService.setPassWord(appUserVo.getId(), password);
        }
        return R.ok("修改成功");
    }

    /**
     * 获取APP的下载地址
     *
     * @return
     */
    @SaIgnore
    @GetMapping("download")
    public R<HashMap<String, String>> download() {
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("ios", iSysConfigService.selectConfigByKey("app.down.ios"));
        hashMap.put("android", iSysConfigService.selectConfigByKey("app.down.android"));
        hashMap.put("downloadUrl", iSysConfigService.selectConfigByKey("app.share.h5"));
        hashMap.put("phone", iSysConfigService.selectConfigByKey("app.info.phone"));
        hashMap.put("weChat", iSysConfigService.selectConfigByKey("app.info.wechat"));
        return R.ok(hashMap);
    }

    /**
     * 创建腾讯用户信息
     *
     * @param user 用户信息
     * @return
     */
    private void createImUser(UserVo user) {
        String code = String.valueOf(user.getId());
        TxTemplate imTemplate = SpringUtils.getBean(TxTemplate.class);
        if (StringUtils.isBlank(user.getUuid( ))) {
            Boolean result = imTemplate.accountImport(code, user.getNickName(), user.getAvatar());
            if (result) {
                log.warn("创建用户成功{}",result);
                user.setUuid(code);
                user.setUserSign(imTemplate.getUserSign(code));
                AppUserBo userBo = new AppUserBo();
                userBo.setId(user.getId());
                userBo.setUuid(code);
                appUserService.updateByBo(userBo);
            }
            AppAssetVo appAssetVo = appAssetService.queryByUid(user.getId());
            if (Objects.isNull(appAssetVo)) {
                appAssetService.insert(user.getId());
            }
        } else {
            user.setUserSign(imTemplate.getUserSign(code));
        }
    }
}
